<template>
  <div v-if="editor" class="editor" ref="editorRoot">
    <BubbleMenu
      v-if="editor"
      :editor="editor"
      :tippy-options="bubbleMenuTippyOptions"
      :should-show="({ editor }) => editor.isActive('image')"
      class="custom-image-toolbar"
    >
      <button
        type="button"
        @click="editor.chain().focus().updateAttributes('image', { textAlign: 'left' }).run()"
        :class="{ 'is-active': editor.getAttributes('image').textAlign === 'left' }"
        title="左对齐"
      >
        <span class="icon">居左</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().updateAttributes('image', { textAlign: 'center' }).run()"
        :class="{ 'is-active': editor.getAttributes('image').textAlign === 'center' }"
        title="居中"
      >
        <span class="icon">居中</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().updateAttributes('image', { textAlign: 'right' }).run()"
        :class="{ 'is-active': editor.getAttributes('image').textAlign === 'right' }"
        title="右对齐"
      >
        <span class="icon">居右</span>
      </button>
      <div class="divider"></div>
      <input
        type="range"
        min="20"
        max="100"
        :value="getImageWidth()"
        @input="setImageWidth"
        class="slider"
      />
      <span class="percent">{{ getImageWidth() }}%</span>
    </BubbleMenu>

    <div class="menu">
      <button
        type="button"
        @click="editor.chain().focus().toggleBold().run()"
        :disabled="!editor.can().chain().focus().toggleBold().run()"
        :class="{ 'is-active': editor.isActive('bold') }"
        title="加粗"
      >
        <span class="icon">𝐁</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleItalic().run()"
        :disabled="!editor.can().chain().focus().toggleItalic().run()"
        :class="{ 'is-active': editor.isActive('italic') }"
        title="斜体"
      >
        <span class="icon">𝐼</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleUnderline().run()"
        :disabled="!editor.can().chain().focus().toggleUnderline().run()"
        :class="{ 'is-active': editor.isActive('underline') }"
        title="下划线"
      >
        <span class="icon">U̲</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleStrike().run()"
        :disabled="!editor.can().chain().focus().toggleStrike().run()"
        :class="{ 'is-active': editor.isActive('strike') }"
        title="删除线"
      >
        <span class="icon">S̶</span>
      </button>
      <!-- <button type="button" @click="editor.chain().focus().toggleSuperscript().run()" :disabled="!editor.can().chain().focus().toggleSuperscript().run()" :class="{ 'is-active': editor.isActive('superscript') }"><span class="icon">x²</span></button>
      <button type="button" @click="editor.chain().focus().toggleSubscript().run()" :disabled="!editor.can().chain().focus().toggleSubscript().run()" :class="{ 'is-active': editor.isActive('subscript') }"><span class="icon">x₂</span></button> -->
      <button
        type="button"
        @click="editor.chain().focus().toggleCode().run()"
        :disabled="!editor.can().chain().focus().toggleCode().run()"
        :class="{ 'is-active': editor.isActive('code') }"
        title="行内代码"
      >
        <span class="icon">行内代码</span>
      </button>
      <button type="button" @click="editor.chain().focus().unsetAllMarks().run()" title="清除格式">
        <span class="icon">清除格式</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().setParagraph().run()"
        :class="{ 'is-active': editor.isActive('paragraph') }"
        title="段落"
      >
        <span class="icon">段落</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
        title="标题1"
      >
        <span class="icon">H₁</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
        title="标题2"
      >
        <span class="icon">H₂</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
        title="标题3"
      >
        <span class="icon">H₃</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleBulletList().run()"
        :class="{ 'is-active': editor.isActive('bulletList') }"
        title="无序列表"
      >
        <span class="icon">无序列表</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleOrderedList().run()"
        :class="{ 'is-active': editor.isActive('orderedList') }"
        title="有序列表"
      >
        <span class="icon">有序列表</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleTaskList().run()"
        :class="{ 'is-active': editor.isActive('taskList') }"
        title="任务列表"
      >
        <span class="icon">任务列表</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleCodeBlock().run()"
        :class="{ 'is-active': editor.isActive('codeBlock') }"
        title="代码块"
      >
        <span class="icon">代码块</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().toggleBlockquote().run()"
        :class="{ 'is-active': editor.isActive('blockquote') }"
        title="引用"
      >
        <span class="icon">引用</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().setHorizontalRule().run()"
        title="分割线"
      >
        <span class="icon">―</span>
      </button>
      <button type="button" @click="editor.chain().focus().setHardBreak().run()" title="换行">
        <span class="icon">↵</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()"
        title="插入表格"
      >
        <span class="icon">田</span>
      </button>
      <button type="button" @click="editor.chain().focus().addColumnBefore().run()" title="前插列">
        <span class="icon">前插列</span>
      </button>
      <button type="button" @click="editor.chain().focus().addColumnAfter().run()" title="后插列">
        <span class="icon">后插列</span>
      </button>
      <button type="button" @click="editor.chain().focus().deleteColumn().run()" title="删除列">
        <span class="icon">删除列</span>
      </button>
      <button type="button" @click="editor.chain().focus().addRowBefore().run()" title="前插行">
        <span class="icon">前插行</span>
      </button>
      <button type="button" @click="editor.chain().focus().addRowAfter().run()" title="后插行">
        <span class="icon">后插行</span>
      </button>
      <button type="button" @click="editor.chain().focus().deleteRow().run()" title="删除行">
        <span class="icon">删除行</span>
      </button>
      <button type="button" @click="editor.chain().focus().deleteTable().run()" title="删除表格">
        <span class="icon">删除表格</span>
      </button>
      <button type="button" @click="setLink" title="插入链接">
        <span class="icon">链接</span>
      </button>
      <button type="button" @click="setImage" title="插入图片">
        <span class="icon">图片</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().undo().run()"
        :disabled="!editor.can().undo()"
        title="撤销"
      >
        <span class="icon">↶</span>
      </button>
      <button
        type="button"
        @click="editor.chain().focus().redo().run()"
        :disabled="!editor.can().redo()"
        title="重做"
      >
        <span class="icon">↷</span>
      </button>
    </div>
    <editor-content :editor="editor" class="editor-content" />
    
    <!-- 新增：插入链接弹窗 -->
    <el-dialog title="插入链接" :visible.sync="linkDialogVisible" width="400px" @close="resetLinkDialog">
      <el-form :model="linkForm" label-width="80px">
        <el-form-item label="链接地址">
          <el-input v-model="linkForm.url" placeholder="请输入链接地址"></el-input>
        </el-form-item>
        <el-form-item label="链接名称">
          <el-input v-model="linkForm.text" placeholder="请输入链接名称"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="linkDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="confirmInsertLink">确定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'

import { Editor, EditorContent, BubbleMenu } from '@tiptap/vue-2'
import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline'
import Superscript from '@tiptap/extension-superscript'
import Subscript from '@tiptap/extension-subscript'
import Link from '@tiptap/extension-link'
import Image from '@tiptap/extension-image'
import TaskList from '@tiptap/extension-task-list'
import TaskItem from '@tiptap/extension-task-item'
import Table from '@tiptap/extension-table'
import TableRow from '@tiptap/extension-table-row'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TextAlign from '@tiptap/extension-text-align'

export default {
  components: {
    EditorContent,
    BubbleMenu
  },

  props: {
    value: {
      type: [String, Object],
      default: () => ({})
    }
  },

  data() {
    return {
      editor: null,
      ydoc: null,
      provider: null,
      awareness: null,
      // 新增：插入链接弹窗相关
      linkDialogVisible: false,
      linkForm: {
        url: '',
        text: ''
      },
      linkInsertFrom: 0,
      linkInsertEmpty: true
    }
  },

  computed: {
    bubbleMenuTippyOptions() {
      return {
        duration: 100,
        getReferenceClientRect: () => {
          const editorRoot = this.$refs.editorRoot
          const { view, state } = this.editor
          const { from } = state.selection
          const img = view.nodeDOM(from)
          if (img && img.tagName === 'IMG' && editorRoot) {
            const imgRect = img.getBoundingClientRect()
            const editorRect = editorRoot.getBoundingClientRect()
            const width = 0
            const height = 0
            const x = editorRect.left + editorRect.width / 2 - width / 2
            const y = imgRect.top + 5
            return {
              width,
              height,
              top: y,
              bottom: y + height,
              left: x,
              right: x + width
            }
          }
          return null
        }
      }
    }
  },

  watch: {
    value(value) {
      // HTML
      // const isSame = this.editor.getHTML() === value

      // JSON
      const isSame = JSON.stringify(this.editor.getJSON()) === JSON.stringify(value)

      if (isSame) {
        return
      }

      this.editor.commands.setContent(value, false)
    }
  },

  mounted() {
    if (!Y || !Y.Doc) {
      console.error('Yjs 没有正确导入！')
      return
    }
    // 1. 创建 Yjs 文档
    this.ydoc = new Y.Doc()
    this.provider = new WebsocketProvider('ws://localhost:1234', 'test-room', this.ydoc)
    this.awareness = this.provider.awareness

    // this.provider.ws.addEventListener('message', event => {
    //   console.log('收到消息:', event.data)
    // })
    // this.provider.ws.addEventListener('send', event => {
    //   console.log('发送消息:', event.data)
    // })

    this.initEditor()
    this.picturePaste()
    if (this.editor) {
      this.editor.on('selectionUpdate', () => {
        this.$forceUpdate()
      })
    }
  },

  beforeDestroy() {
    this.editor.destroy()
  },

  methods: {
    initEditor() {
      this.editor = new Editor({
        content: this.value,
        extensions: [
          StarterKit,
          Underline,
          Superscript,
          Subscript,
          Link.configure({
            autolink: true,
            openOnClick: true,
            linkOnPaste: true,
            HTMLAttributes: {
              rel: 'noopener noreferrer',
              target: '_blank',
              class: 'tiptap-link'
            }
          }),
          Image.configure({
            inline: false,
            allowBase64: true
          }).extend({
            addAttributes() {
              return {
                ...this.parent?.(),
                width: {
                  default: '50%',
                  parseHTML: element => element.getAttribute('width'),
                  renderHTML: attributes => ({ width: attributes.width })
                },
                height: {
                  default: 'auto',
                  parseHTML: element => element.getAttribute('height'),
                  renderHTML: attributes => ({ height: attributes.height })
                },
                textAlign: {
                  default: 'center',
                  parseHTML: element => element.getAttribute('data-text-align') || 'center',
                  renderHTML: attributes => ({
                    'data-text-align': attributes.textAlign || 'center'
                  })
                }
              }
            }
          }),
          TaskList,
          TaskItem,
          Table.configure({ resizable: true }),
          TableRow,
          TableHeader,
          TableCell,
          TextAlign.configure({
            types: ['heading', 'paragraph', 'image']
          }),
          Collaboration.configure({
            document: this.ydoc // 传入 Yjs 文档
          }),
          CollaborationCursor.configure({
            provider: this.provider,
            user: {
              name: this.$store?.state?.user?.realName || '未知用户',
              color: (() => {
                // 生成一个不在深蓝色区间的色相
                let hue = Math.floor(Math.random() * 360)
                // 避开200~260的深蓝色区间
                if (hue >= 200 && hue <= 260) {
                  hue = (hue + 70) % 360
                }
                return `hsl(${hue}, 90%, 72%)`
              })(),
            },
            render: user => {
              const wrapper = document.createElement('span')
              wrapper.style.position = 'relative'
              wrapper.style.display = 'inline-block'
              wrapper.innerHTML = `
              <span class="collab-cursor-wrapper">               
                <p class="collab-cursor-bar" style="background:${user.color};"></p>
                <span class="collab-cursor-bubble" style="background:${user.color};">${user.name}</span>
              </span>
              `
              return wrapper
            }
          })
        ],
        onUpdate: () => {
          // HTML
          // this.$emit('input', this.editor.getHTML())

          // JSON
          this.$emit('input', this.editor.getJSON())
        }
      })
    },
    setLink() {
      const { state } = this.editor
      const { from, empty } = state.selection
      this.linkInsertFrom = from
      this.linkInsertEmpty = empty
      this.linkForm.url = ''
      this.linkForm.text = ''
      // 如果有选中文本，自动填充名称
      if (!empty) {
        const selectedText = this.editor.state.doc.textBetween(state.selection.from, state.selection.to, ' ')
        this.linkForm.text = selectedText
      }
      this.linkDialogVisible = true
    },
    confirmInsertLink() {
      const url = this.linkForm.url
      const linkText = this.linkForm.text || '新链接'
      if (!url) {
        this.$message.warning('请输入链接地址')
        return
      }
      if (this.linkInsertEmpty) {
        // 没有选中文本，插入新文本并加链接
        this.editor.chain().focus().insertContent(linkText).run()
        this.editor.chain().focus().setTextSelection({ from: this.linkInsertFrom, to: this.linkInsertFrom + linkText.length - 1 }).run()
        this.editor.chain().focus().setLink({ href: url }).run()
      } else {
        // 已选中文本，加链接
        this.editor.chain().focus().setLink({ href: url }).run()
      }
      this.linkDialogVisible = false
      this.resetLinkDialog()
    },
    resetLinkDialog() {
      this.linkForm.url = ''
      this.linkForm.text = ''
      this.linkInsertFrom = 0
      this.linkInsertEmpty = true
    },
    setImage() {
      const url = window.prompt('请输入图片地址')
      if (url) {
        this.editor.chain().focus().setImage({ src: url }).run()
      }
    },
    picturePaste() {
      // 监听粘贴图片
      this.$nextTick(() => {
        const dom = this.editor.view.dom
        dom.addEventListener('paste', e => {
          const items = e.clipboardData && e.clipboardData.items
          if (!items) return
          for (let i = 0; i < items.length; i++) {
            const item = items[i]
            if (item.type.indexOf('image') !== -1) {
              const file = item.getAsFile()
              const reader = new FileReader()
              reader.onload = evt => {
                // 直接插入图片，不再弹窗
                this.editor.chain().focus().setImage({ src: evt.target.result }).run()
              }
              reader.readAsDataURL(file)
              e.preventDefault()
            }
          }
        })
      })
    },
    getImageWidth() {
      const { width } = this.editor.getAttributes('image')
      if (typeof width === 'string' && width.endsWith('%')) {
        return parseInt(width, 10)
      }
      // A default or calculated value if width is not a percentage
      return 100
    },
    setImageWidth(event) {
      const width = `${event.target.value}%`
      this.editor.chain().focus().updateAttributes('image', { width }).run()
      this.editor.commands.focus()
    }
  }
}
</script>

<style lang="scss">
/* Basic editor styles */
.tiptap {
  * {
    position: relative;
  }
  > * + * {
    margin-top: 0.75em;
  }

  ul,
  ol {
    padding: 0 1rem;
  }

  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    line-height: 1.1;
  }

  p {
    padding: 0;
    margin: 0;
    margin-bottom: 5px;
    line-height: 1.5;
    letter-spacing: 0.08em;
  }
  code {
    background-color: rgba(#616161, 0.1);
    color: #616161;
  }

  pre {
    background: #0d0d0d;
    color: #fff;
    font-family: 'JetBrainsMono', monospace;
    padding: 0.75rem 1rem;
    border-radius: 0.5rem;

    code {
      color: inherit;
      padding: 0;
      background: none;
      font-size: 0.8rem;
    }
  }

  blockquote {
    padding-left: 1rem;
    border-left: 2px solid rgba(#0d0d0d, 0.1);
  }

  hr {
    border: none;
    border-top: 2px solid rgba(#0d0d0d, 0.1);
    margin: 2rem 0;
  }
}

.tiptap table {
  border-collapse: collapse;
  width: 100%;
  margin: 1em 0;
  background: #fff;
}

.tiptap th,
.tiptap td {
  border: 1px solid #dcdcdc;
  padding: 8px 12px;
  text-align: left;
}

.tiptap th {
  background: #f5f5f5;
  font-weight: bold;
}

.tiptap tr:nth-child(even) {
  background: #fafafa;
}

.tiptap tr:hover {
  background: #f0f7ff;
}

/* 链接样式 */
.tiptap-link {
  color: #1976d2;
  text-decoration: underline;
  cursor: pointer;
  transition: color 0.2s;
}

.tiptap-link:hover {
  color: #d81b60;
}

/* 图片样式 */
.tiptap img {
  max-width: 100%;
  height: auto;
  cursor: pointer;
  margin: 20px;
  display: inline-block;
  overflow: hidden;
}

.tiptap img.ProseMirror-selectednode {
  outline: 2px solid #58a4f1; // 你可以换成你喜欢的颜色
  outline-offset: 2px;
  border-radius: 4px;
  box-shadow: 0 0 0 2px #fff; // 可选，让边框更明显
  z-index: 1000;
}

.tiptap p.has-text-align-left,
.tiptap h1.has-text-align-left,
.tiptap h2.has-text-align-left,
.tiptap h3.has-text-align-left {
  text-align: left;
}

.tiptap p.has-text-align-center,
.tiptap h1.has-text-align-center,
.tiptap h2.has-text-align-center,
.tiptap h3.has-text-align-center {
  text-align: center;
}

.tiptap p.has-text-align-right,
.tiptap h1.has-text-align-right,
.tiptap h2.has-text-align-right,
.tiptap h3.has-text-align-right {
  text-align: right;
}

.tiptap img[data-text-align='left'] {
  display: block;
  margin: 0 auto 0 0;
}
.tiptap img[data-text-align='center'] {
  display: block;
  margin: 0 auto;
}
.tiptap img[data-text-align='right'] {
  display: block;
  margin: 0 0 0 auto;
}

.bubble-menu {
  display: flex;
  align-items: center;
  background-color: #333;
  color: #fff;
  padding: 0.5rem;
  border-radius: 5px;

  button {
    background: none;
    border: none;
    color: #fff;
    cursor: pointer;
    margin-right: 0.5rem;
    padding: 4px 8px;

    &.is-active {
      background-color: #555;
      border-radius: 3px;
    }
  }

  input[type='range'] {
    margin: 0 0.5rem;
    cursor: pointer;
  }

  span {
    font-size: 0.8rem;
    min-width: 30px;
  }
}
.editor {
  border: 1px solid #ccc;
  border-radius: 5px;
  height: 100%;
  display: flex;
  flex-direction: column;

  .menu {
    display: flex;
    flex-wrap: wrap;
    padding: 0.5rem;
    border-bottom: 1px solid #ccc;

    button {
      margin: 2px 2px;
      border: none;
      background-color: #fff;
      cursor: pointer;
      padding: 0 10px;
      border-radius: 6px;
      height: 32px;
      min-width: 32px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 18px;
      color: #222;
      transition: background 0.15s, color 0.15s;
      box-shadow: none;

      &.is-active {
        background-color: #ededed;
        color: #222;
      }
      &:hover:not(:disabled):not(.is-active) {
        background-color: #f5f5f5;
      }
      &:disabled {
        opacity: 0.5;
        cursor: not-allowed;
      }
      .icon {
        font-family: inherit;
        font-size: 18px;
        font-weight: bold;
        letter-spacing: 0.05em;
        pointer-events: none;
      }
    }
  }

  .editor-content {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: auto;
    padding: 20px 15px 15px 15px;
  }
}

.tiptap {
  min-height: 200px;
  flex: 1 1 0;
  height: 100%;
  overflow: auto;

  &:focus {
    outline: none;
  }
}

.custom-image-toolbar {
  display: flex;
  align-items: center;
  background: #fff;
  color: #222;
  padding: 0.25rem 0.25rem;
  border-radius: 8px;
  border: 1px solid #e0e0e0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  min-height: 36px;
  min-width: 180px;
  font-size: 16px;
  visibility: visible!important;

  button {
    background: none;
    border: none;
    color: #222;
    cursor: pointer;
    margin: 0 2px;
    padding: 0 8px;
    height: 32px;
    border-radius: 6px;
    font-size: 16px;
    display: flex;
    align-items: center;
    transition: background 0.15s;

    &.is-active {
      background: #ededed;
      color: #222;
    }
    &:hover:not(.is-active) {
      background: #f5f5f5;
    }
    .icon {
      font-family: inherit;
      font-size: 16px;
      font-weight: bold;
      letter-spacing: 0.1em;
    }
  }

  .divider {
    width: 1px;
    height: 22px;
    background: #e0e0e0;
    margin: 0 10px;
  }

  .slider {
    -webkit-appearance: none;
    width: 90px;
    height: 8px;
    background: #ededed;
    border-radius: 4px;
    outline: none;
    margin: 0 8px;
    transition: background 0.2s;
    vertical-align: middle;
    position: relative;
  }
  .slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 10px;
    height: 18px;
    border-radius: 8px;
    background: #222;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
    border: none;
    margin-top: -5px;
  }
  .slider::-moz-range-thumb {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #222;
    cursor: pointer;
    border: none;
  }
  .slider::-ms-thumb {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #222;
    cursor: pointer;
    border: none;
  }
  .slider::-webkit-slider-runnable-track {
    height: 8px;
    border-radius: 4px;
    background: #ededed;
  }
  .slider::-ms-fill-lower {
    background: #ededed;
    border-radius: 4px;
  }
  .slider::-ms-fill-upper {
    background: #ededed;
    border-radius: 4px;
  }
  .slider:focus {
    outline: none;
  }
  .slider:focus::-webkit-slider-thumb {
    box-shadow: 0 0 0 2px #bdbdbd;
  }
  .slider:focus::-ms-thumb {
    box-shadow: 0 0 0 2px #bdbdbd;
  }
  .slider:focus::-moz-range-thumb {
    box-shadow: 0 0 0 2px #bdbdbd;
  }
  .percent {
    color: #7b8a9c;
    font-size: 14px;
    min-width: 36px;
    text-align: right;
  }
}

.collab-cursor-wrapper {
  all: unset;
  font-weight: normal !important;
  font-style: normal !important;
  font-family: Arial, sans-serif !important;
  color: #222 !important;
  text-decoration: none !important;
  background: initial !important;


  display: inline-flex;
  align-items: center;
  width: 2px;
  position: relative;
  pointer-events: none; // 避免影响编辑
  z-index: 1000;

  .collab-cursor-bar {
    all: unset;
    position: absolute; // 关键
    top: 0;
    width: 2px;
    height: 1.1em;
    background: red; // 或动态色
    z-index: 10;
    animation: blink-cursor 1s steps(1) infinite;
    pointer-events: none;
  }

  .collab-cursor-bubble {
    all: unset;
    margin-left: 3px;
    white-space: nowrap;
    position: relative;
    padding: 2px 5px;
    font-size: 12px;
    border-radius: 3px;
    top: -22px;
    left: -3px;
  }
}

@keyframes blink-cursor {
  0%,
  50% {
    opacity: 1;
  }
  51%,
  100% {
    opacity: 0;
  }
}
</style>
